#include "znx_http_session_pool.h"
#include "event/znx_worker_proc.h"


typedef struct {
    int64_t                 max_conns;
    int64_t                 current;
    znx_queue_t             head;
} znx_http_server_session_pool_t;


static int64_t g_server_session_pool_max_conns = 0;
__thread znx_http_server_session_pool_t *t_server_session_pool = NULL;


static void
znx_t_server_session_destroy(void *data)
{
    znx_http_server_session_pool_t *pool = data;
    znx_thread_free(pool);
}


static znx_bool_t
znx_t_server_session_pool_init_handle(void *data)
{
    znx_event_loop_data_t *loop_data = data;
    t_server_session_pool = znx_thread_calloc(sizeof(znx_http_server_session_pool_t));
    t_server_session_pool->max_conns = g_server_session_pool_max_conns;
    znx_queue_init(&t_server_session_pool->head);

    znx_cleanup_list_add(&loop_data->cleanup_list, znx_t_server_session_destroy, t_server_session_pool);

    return ZNX_TRUE;
}


void
znx_http_server_session_init(int64_t max_conns)
{
    if (max_conns == 0) {
        return;
    }

    g_server_session_pool_max_conns = max_conns;
    znx_worker_processor_add_init_hook(znx_t_server_session_pool_init_handle);
}



#if 0

static void
znx_http_server_session_read_handler(znx_event_t *event)
{
    znx_conn_t  *c  = event->data;

    if (event->timedout) {
        c->timedout = 1;
        event->timedout = 0;
        znx_net_conn_close_and_free(c);
    }
}


void
znx_http_server_session_pool_put(znx_conn_t *c, int64_t idle_timeout_ms)
{
    if (c->type != ZNX_TCP_CONN || c->type != ZNX_UDP_CONN) {
        ZNX_DEF_LOG_FATAL(NULL, "expected conn of type net_conn");
        return;
    }

    znx_http_conn_t     *fake_hc;
    fake_hc = znx_thread_calloc(sizeof(znx_http_conn_t));
    znx_http_conn_init(fake_hc, c);

    if (c->l == NULL || c->timedout ||
        c->read.eof || c->read.error ||
        c->write.eof || c->write.error)
    {
        znx_http_conn_close(c);
        return;
    }

    c->read.handler = znx_http_server_session_read_handler;
    znx_event_loop_add_timer(&c->read, idle_timeout_ms);
    znx_event_loop_enable_read(c);
}

#endif


extern void znx_http_fake_hc_free_handle(znx_http_conn_t *fake_hc);


static void
znx_http_server_session_read_handler(znx_http_conn_t *hc)
{
    znx_queue_remove(&hc->queue);
    t_server_session_pool->current--;

    if (hc->c->read.timedout) {
        znx_http_conn_close(hc, znx_http_fake_hc_free_handle);
        return;
    }

    hc->keepalive_handler(hc);
}

void
znx_http_server_session_pool_put(znx_http_conn_t *hc, int64_t idle_timeout_ms)
{
    znx_conn_t          *c;
    znx_queue_t         *q;
    znx_http_conn_t     *last_hc;

    c = hc->c;

    if (hc->keepalive_handler == NULL ||
        c->read.eof || c->read.error ||
        c->write.eof || c->write.error)
    {
        znx_http_conn_close(hc, znx_http_fake_hc_free_handle);
        return;
    }

    if (c->read.eof || c->read.error ||
        c->write.eof || c->write.error ||
        c->timedout)
    {
        znx_http_conn_close(hc, znx_http_fake_hc_free_handle);
        return;
    }

    if (t_server_session_pool->max_conns <= 0) {
        znx_http_conn_close(hc, znx_http_fake_hc_free_handle);
        return;
    }

    if (t_server_session_pool->current >= t_server_session_pool->max_conns) {
        q = znx_queue_last(&t_server_session_pool->head);
        last_hc = znx_queue_data(q, znx_http_conn_t, queue);
        znx_queue_remove(q);
        znx_http_conn_close(last_hc, znx_http_fake_hc_free_handle);
        t_server_session_pool->current--;
    }

    hc->read_handler = znx_http_server_session_read_handler;
    znx_http_conn_enable_read(hc);
    znx_event_loop_add_timer(&c->read, idle_timeout_ms);

    znx_queue_insert_head(&t_server_session_pool->head, &hc->queue);
    t_server_session_pool->current++;
}

